﻿using System;
using System.Diagnostics.Contracts;
using System.Threading;

namespace MathService
{
    public class AnimatedObject
    {
        private readonly int f_SamplesCount;
        private readonly int f_TimeOut;
        private readonly Action<int, int> f_Animator;

        private Thread f_AnimationThread;
        private bool f_Enable;

        public bool Enable
        {
            get { return f_Enable; }
            set { if(value) Start(); else Stop(); }
        }

        public AnimatedObject(Action<int, int> Animator, int SamplesCount, int TimeOut)
        {
            Contract.Requires(Animator != null);
            Contract.Requires(SamplesCount > 0);
            Contract.Requires(TimeOut >= 0);

            f_Animator = Animator;
            f_SamplesCount = SamplesCount;
            f_TimeOut = TimeOut;
        }

        public void Start()
        {
            if(f_Enable) return;
            lock(this)
            {
                if(f_Enable) return;
                f_AnimationThread = new Thread(Animate);
                f_Enable = true;
                f_AnimationThread.Start();
            }
        }

        public void Stop()
        {
            if(!f_Enable) return;
            lock(this)
            {
                if(!f_Enable) return;
                f_Enable = false;
                if(!f_AnimationThread.Join(2 * f_TimeOut) && f_AnimationThread.IsAlive)
                    f_AnimationThread.Abort();
                f_AnimationThread = null;
            }
        }

        private void Animate()
        {
            var count = f_SamplesCount;
            var sleep = f_TimeOut;
            for(var i = 0; i < count && f_Enable; i++)
            {
                f_Animator(i, count);
                Thread.Sleep(sleep);
            }
        }

        [ContractInvariantMethod]
        private void CheckAnimationMethod() { Contract.Invariant(f_Animator != null); }
    }
}
